package com.copm.ifm.servers.sys.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.copm.ifm.base.basic.constant.DbConstants;
import com.copm.ifm.base.basic.constant.NumberConstants;
import com.copm.ifm.base.basic.exception.ServiceException;
import com.copm.ifm.base.basic.msg.ExceptionMsg;
import com.copm.ifm.base.basic.pojo.vo.BaseTreeVO;
import com.copm.ifm.base.basic.util.AuthUtil;
import com.copm.ifm.base.service.tree.TreeHandler;
import com.copm.ifm.base.service.tree.TreeUtil;
import com.copm.ifm.servers.sys.dto.SysCategoryTypeDTO;
import com.copm.ifm.servers.sys.entity.SysCategoryType;
import com.copm.ifm.servers.sys.mapper.SysCategoryTypeMapper;
import com.copm.ifm.servers.sys.service.SysCategoryTypeService;
import com.copm.ifm.servers.sys.vo.SysCategoryTypeVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 类型分类表(5张表总表，根据type进行区分) 服务实现类
 *
 * @author tp
 * @since 2021-01-03
 */
@Slf4j
@Service
public class SysCategoryTypeServiceImpl extends ServiceImpl<SysCategoryTypeMapper, SysCategoryType> implements SysCategoryTypeService {

    @Resource
    SysCategoryTypeMapper sysCategoryTypeMapper;

    @Autowired
    SysCategoryTypeService sysCategoryTypeService;

    @Override
    public BaseTreeVO getEquTypeTree(int enabled, String equName, String type) {
        List<SysCategoryType> sysCategoryTypeList = sysCategoryTypeMapper.getEquTypeTree(enabled, type);
        if (StringUtils.isEmpty(sysCategoryTypeList)) {
            return null;
        }
        List<String> pathList = new ArrayList<>();
        List<SysCategoryType> sysCategoryTypeLists = new ArrayList<>();
        //这里考虑如果只查询无效的选项功能
        if (enabled == 0) {
            sysCategoryTypeList.forEach(sysCategoryType -> {
                String path = sysCategoryType.getPath();
                for (int i = 0; i < sysCategoryType.getLevel(); i++) {
                    pathList.add(path.substring(0, 4 * (i + 1)));
                }
                List<String> list = stringDuplicate(pathList);
                List<SysCategoryType> parentList = sysCategoryTypeMapper.sysCategoryTypePath(list, sysCategoryType.getCategoryType());
                sysCategoryTypeLists.addAll(parentList);
            });
        }
        sysCategoryTypeList.addAll(sysCategoryTypeLists);
        //对象去重的方式
        List<SysCategoryType> sysCategoryTypes = sysCategoryTypeList.stream().collect(Collectors.collectingAndThen
                (Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(SysCategoryType::getId))), ArrayList::new));
        List<SysCategoryType> resultList;
        if (!StringUtils.isEmpty(equName)) {
            List<SysCategoryType> collect = sysCategoryTypes.stream()
                    .filter(s -> s.getName().contains(equName))
                    .collect(Collectors.toList());
            resultList = new ArrayList<>(collect);
            collect.forEach(s -> this.getParent(s, sysCategoryTypes, resultList));
        } else {
            resultList = sysCategoryTypes;
        }
        //set方式进行去重
        return TreeHandler.of(SysCategoryTypeVO.class).getTree(new HashSet<>(resultList));
    }


    /**
     * String方式List去重的方式
     */
    public static List<String> stringDuplicate(List<String> list) {
        HashSet<String> h = new HashSet<>(list);
        list.clear();
        list.addAll(h);
        return list;
    }

    @Override
    public SysCategoryTypeVO selectById(int id) {
        SysCategoryTypeVO sysCategoryTypeVO = sysCategoryTypeMapper.selectVoById(id);
        if (StringUtils.isEmpty(sysCategoryTypeVO)) {
            throw new ServiceException(ExceptionMsg.ENTITY_NOT_EXISTS);
        }
        sysCategoryTypeVO.setUpdateBy(AuthUtil.getCurrentUserId());
        sysCategoryTypeVO.setParentPathName(NumberConstants.NONE);
        String path = sysCategoryTypeVO.getPath();
        List<String> list = new ArrayList<>();
        for (int i = 1; i < sysCategoryTypeVO.getLevel(); i++) {
            list.add(path.substring(0, 4 * i));
        }
        list.add(path);
        List<String> codeList = sysCategoryTypeMapper.listByPath(list, sysCategoryTypeVO.getCategoryType());
        String fullPath = codeList.stream().map(String::valueOf).collect(Collectors.joining("-"));
        sysCategoryTypeVO.setFullCode(fullPath);
        if (!StringUtils.isEmpty(sysCategoryTypeVO)) {
            SysCategoryType sysCategoryTypeParent = sysCategoryTypeMapper.selectOne(new LambdaQueryWrapper<SysCategoryType>()
                    .eq(SysCategoryType::getPath, sysCategoryTypeVO.getParentPath())
                    .eq(SysCategoryType::getCategoryType, sysCategoryTypeVO.getCategoryType())
                    .eq(SysCategoryType::getTenantEnabled, 1)
                    .eq(SysCategoryType::getEnabled, 1)
                    .eq(SysCategoryType::getDeleted, 0));
            if (!StringUtils.isEmpty(sysCategoryTypeParent)) {
                sysCategoryTypeVO.setParentPathName(sysCategoryTypeParent.getName());
                sysCategoryTypeVO.setParentId(sysCategoryTypeParent.getId());
            }
        }
        return sysCategoryTypeVO;
    }

    /**
     * 1.根据父级path查找当前层级最大的path（比如：00010007是最大的）；
     * 2.使用方法生成特定的path路径，都是四位的数字生成code；通过最大的maxPath生成加1的path路径；
     * 3.根据name和code进行校验；
     * 4.DTO接受参数类转换成PO对象，设置path？
     * 5.添加并修改父级叶节点为false
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized SysCategoryType add(SysCategoryTypeDTO addDTO) {
        String categoryType = addDTO.getCategoryType();
        String parentPath = addDTO.getParentPath();
        String maxPath = sysCategoryTypeMapper.selectMaxPath(parentPath, categoryType);
        Integer userId = AuthUtil.getCurrentUserId();
        //校验该code是否存在,校验该name和code是查询全部
        List<SysCategoryType> sysCategoryType = sysCategoryTypeMapper.selectList(new LambdaQueryWrapper<SysCategoryType>()
                .eq(SysCategoryType::getDeleted, 0)
                .eq(SysCategoryType::getCode, addDTO.getCode())
                .eq(SysCategoryType::getCategoryType, addDTO.getCategoryType())
                .eq(SysCategoryType::getTenantEnabled, 1)
                .eq(SysCategoryType::getParentPath, addDTO.getParentPath()));
        if (sysCategoryType.size() > 0) {
            throw new ServiceException(ExceptionMsg.ADD_ALREADY_EXISTS, addDTO.getCode());
        }
        List<SysCategoryType> sysCategoryTypeName = sysCategoryTypeMapper.selectList(new LambdaQueryWrapper<SysCategoryType>()
                .eq(SysCategoryType::getDeleted, 0)
                .eq(SysCategoryType::getCategoryType, addDTO.getCategoryType())
                .eq(SysCategoryType::getName, addDTO.getName())
                .eq(SysCategoryType::getTenantEnabled, 1)
                .eq(SysCategoryType::getParentPath, addDTO.getParentPath()));
        if (sysCategoryTypeName.size() > 0) {
            throw new ServiceException(ExceptionMsg.ADD_ALREADY_EXISTS, addDTO.getName());
        }
        //添加
        SysCategoryType entity = new SysCategoryType();
        BeanUtils.copyProperties(addDTO, entity);
        //设置本身路径，通过父路径可能含有多个，但是如果没有的话就说明这个第一个
        if (StringUtils.isEmpty(maxPath)) {
            entity.setPath(TreeUtil.getFirstChildPath(entity.getParentPath()));
        } else {
            entity.setPath(TreeUtil.getNextChildPath(maxPath));
        }
        entity.setName(addDTO.getName());
        entity.setLevel(TreeUtil.getLevel(entity.getPath()));
        entity.setCreateTime(new Date());
        entity.setLeafNode(addDTO.getLeafNode());
        entity.setCreateBy(userId);
        entity.setUpdateBy(userId);
        entity.setCreateTime(new Date());
        entity.setUpdateTime(new Date());
        sysCategoryTypeMapper.insert(entity);

        //添加子分类之后，设置父节点的叶子节点为不是叶子节点，根据path修改
        if (StringUtils.isEmpty(maxPath)) {
            SysCategoryType temp = new SysCategoryType();
            temp.setLeafNode(false);
            temp.setPath(entity.getParentPath());
            temp.setCategoryType(entity.getCategoryType());
            sysCategoryTypeMapper.updateLeafNode(temp);
        }
        return entity;
    }

    /**
     * 编辑修改保存，根据前端传来的对象进行保存
     * 条件：
     * 1.判断name是否重复，但是在同级别的重复不行，但是在不同级别的重复是不允许的；
     * 2.从有效改成无效
     * 1>如果这个对象是有子节点的，那么先判断子节点是否都是无效的，一旦有一个是有效的，都不能修改
     * 2>那么如果这个对象柚子节点，且子节点都是无效的，那就ok
     * 3.从无效变成有效
     * 1>判断父级是不是有效的，如果父级是无效的，那么也是不可以修改
     * 4.修改操作。
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized SysCategoryType modify(SysCategoryTypeDTO entity) {
        //校验名称：同层级的不可以重复，不同层级的重复可以重复
        List<SysCategoryType> subOptList;
        if (!StringUtils.isEmpty(entity.getParentPath())) {
            subOptList = sysCategoryTypeMapper.selectList(new LambdaQueryWrapper<SysCategoryType>()
                    .eq(SysCategoryType::getDeleted, 0)
                    .eq(SysCategoryType::getName, entity.getName())
                    .eq(SysCategoryType::getCategoryType, entity.getCategoryType())
                    .eq(SysCategoryType::getParentPath, entity.getParentPath())
                    .eq(SysCategoryType::getTenantEnabled, 1));
        } else {
            subOptList = sysCategoryTypeMapper.selectList(new LambdaQueryWrapper<SysCategoryType>()
                    .eq(SysCategoryType::getDeleted, 0)
                    .eq(SysCategoryType::getName, entity.getName())
                    .eq(SysCategoryType::getCategoryType, entity.getCategoryType())
                    .eq(SysCategoryType::getLevel, entity.getLevel())
                    .eq(SysCategoryType::getTenantEnabled, 1));
        }
        boolean b = false;
        if (subOptList.size() >= 1) {
            b = subOptList.get(0).getId().equals(entity.getId());
        }
        if (subOptList.size() > 0 && !b) {
            throw new ServiceException(ExceptionMsg.ADD_ALREADY_EXISTS, entity.getName());
        }

        SysCategoryType oldSysCategoryType = sysCategoryTypeMapper.selectById(entity.getId());
        boolean changeToDisable = (oldSysCategoryType.getEnabled() == 1) && (entity.getEnabled() == 0);
        List<SysCategoryType> sysCategoryType = sysCategoryTypeMapper.selectList(new LambdaQueryWrapper<SysCategoryType>()
                .eq(SysCategoryType::getDeleted, 1)
                .eq(SysCategoryType::getCategoryType, oldSysCategoryType.getCategoryType())
                .eq(SysCategoryType::getParentPath, oldSysCategoryType.getPath())
                .eq(SysCategoryType::getTenantEnabled, 0));
        boolean hasEnableChild = StringUtils.isEmpty(sysCategoryType);
        if (!entity.getLeafNode() && changeToDisable && hasEnableChild) {
            throw new ServiceException("该设备设施分项下存在有效的子分项，不允许失效");
        }

        boolean changeToEnable = (oldSysCategoryType.getEnabled() == 0) && (entity.getEnabled() == 1);
        SysCategoryType sysCategoryTypParent = sysCategoryTypeMapper.selectOne(new LambdaQueryWrapper<SysCategoryType>()
                .eq(SysCategoryType::getDeleted, 0)
                .eq(SysCategoryType::getEnabled, 0)
                .eq(SysCategoryType::getCategoryType, oldSysCategoryType.getCategoryType())
                .eq(SysCategoryType::getPath, oldSysCategoryType.getPath()));
        //将失效的分项改为有效时，需校验其所有父级设施分项是否有效，当存在失效的父级设施分项时，提示“存在失效的父级分项，请先将修改父级分项为有效”；
        if (changeToEnable && !StringUtils.isEmpty(entity.getParentPath()) && StringUtils.isEmpty(sysCategoryTypParent)) {
            throw new ServiceException("存在失效的父级分项，请先将修改父级分项为有效");
        }
        SysCategoryType sysEntity = new SysCategoryType();
        BeanUtils.copyProperties(entity, sysEntity);
        sysEntity.setUpdateBy(AuthUtil.getCurrentUserId());
        sysCategoryTypeMapper.updateById(sysEntity);
        return sysEntity;
    }

    /**
     * 软删除的方式更新delete状态，是否删除 [0未删除 1已删除]
     * 1.根据id判断删除的对象是否存在；
     * 2.判断该对象是否有可用的子节点，如果有则不允许删除；
     * 3.在不是第一层级的情况下，如果子节点都删除了，那么就要更新自己的父级为叶子节点；
     * 4.修改当前id对象的删除状态；
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateDelete(int id) {
        SysCategoryType sysCategoryType = sysCategoryTypeMapper.selectById(id);
        if (StringUtils.isEmpty(sysCategoryType)) {
            throw new ServiceException("节点不存在，请刷新后再试！");
        }
        if (!sysCategoryType.getLeafNode()) {
            throw new ServiceException("不是叶子节点，不允许直接删除，请先删除其子节点！");
        }
        sysCategoryType.setDeleted(NumberConstants.CONSTANTS_ONE);
        sysCategoryType.setUpdateBy(AuthUtil.getCurrentUserId());
        sysCategoryTypeMapper.updateById(sysCategoryType);

        // 若删除的不是第一层级，则需更新其父节点为叶子节点
        if (!sysCategoryType.getLevel().equals(NumberConstants.CONSTANTS_ONE)) {
            Integer count = sysCategoryTypeMapper.selectCount(new LambdaQueryWrapper<SysCategoryType>()
                    .eq(SysCategoryType::getDeleted, 0)
                    .eq(SysCategoryType::getEnabled, 1)
                    .eq(SysCategoryType::getCategoryType, sysCategoryType.getCategoryType())
                    .eq(SysCategoryType::getParentPath, sysCategoryType.getParentPath())
                    .eq(SysCategoryType::getTenantEnabled, 1));
            if (count == 0) {
                SysCategoryType sysEquipment = new SysCategoryType();
                sysEquipment.setPath(sysCategoryType.getParentPath());
                sysEquipment.setCategoryType(sysCategoryType.getCategoryType());
                sysEquipment.setLeafNode(true);
                sysCategoryTypeMapper.updateByParentPath(sysEquipment);
            }
        }
    }

    /**
     * 软删除的方式更新delete状态，是否删除 [0未删除 1已删除]
     * 1.根据id判断删除的对象是否存在；
     * 2.直接删除所有的子节点，包括该父节点。
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateParentDelete(int id) {
        SysCategoryType sysCategoryType = sysCategoryTypeMapper.selectById(id);
        if (StringUtils.isEmpty(sysCategoryType)) {
            throw new ServiceException("节点不存在，请刷新后再试！");
        }
        sysCategoryType.setDeleted(NumberConstants.CONSTANTS_ONE);
        sysCategoryType.setUpdateBy(AuthUtil.getCurrentUserId());
        sysCategoryTypeMapper.updateById(sysCategoryType);
        List<SysCategoryType> sysCategoryTypeList = sysCategoryTypeMapper
                .selectList(new LambdaQueryWrapper<SysCategoryType>()
                        .eq(SysCategoryType::getParentPath, sysCategoryType.getPath())
                        .eq(SysCategoryType::getCategoryType, sysCategoryType.getCategoryType())
                        .eq(SysCategoryType::getDeleted, DbConstants.DEFAULT_NO)
                );
        sysCategoryTypeList.forEach(s -> {
            s.setDeleted(DbConstants.DEFAULT_YES);
        });
        updateBatchById(sysCategoryTypeList);
    }

    @Override
    public int countById(int id) {
        return sysCategoryTypeMapper.countById(id);
    }


    /**
     * 点击添加返回给前端的封装数据
     * 当父级ID为空的情况下就是增加一级节点，有父级ID就是增加下级节点
     */
    @Override
    public SysCategoryTypeVO clickAddEqu(Integer parentEquId, String type) {
        SysCategoryTypeVO sysCategoryTypeVO = new SysCategoryTypeVO();
        //首先默认为第一层级的分类
        int level = 1;
        //父级ID不为空的情况下，即为增加下级分类
        SysCategoryTypeVO parentEqu = new SysCategoryTypeVO();
        if (!StringUtils.isEmpty(parentEquId)) {
            parentEqu = sysCategoryTypeMapper.selectParentEquById(parentEquId, type);
            sysCategoryTypeVO.setParentPath(parentEqu.getPath());
            sysCategoryTypeVO.setParentPathName(parentEqu.getParentPathName());
            level = parentEqu.getLevel() + 1;
        }
        Integer maxSeq = sysCategoryTypeMapper.selectMaxSeq(parentEqu.getPath(), level, type);
        sysCategoryTypeVO.setOrderNum(StringUtils.isEmpty(maxSeq) ? 10 : maxSeq / 10 * 10 + 10);
        sysCategoryTypeVO.setEnabled(1);
        return sysCategoryTypeVO;
    }

    /**
     * 获取设备设施表树状结构 - 获取父级
     */
    private void getParent(SysCategoryType subdivisionOption, List<SysCategoryType> list, List<SysCategoryType> resultList) {
        String parentPath = subdivisionOption.getParentPath();
        if (StringUtils.isEmpty(parentPath)) {
            return;
        }
        for (SysCategoryType s : list) {
            if (parentPath.equals(s.getPath())) {
                resultList.add(s);
                if (!StringUtils.isEmpty(parentPath)) {
                    getParent(s, list, resultList);
                }
            }
        }
    }
}
